/*
 * "$Id: testmxml.c 451 2014-01-04 21:50:06Z msweet $"
 *
 * Test program for Mini-XML, a small XML-like file parsing library.
 *
 * Copyright 2003-2014 by Michael R Sweet.
 *
 * These coded instructions, statements, and computer programs are the
 * property of Michael R Sweet and are protected by Federal copyright
 * law.  Distribution and use rights are outlined in the file "COPYING"
 * which should have been included with this file.  If this file is
 * missing or damaged, see the license at:
 *
 *     http://www.msweet.org/projects.php/Mini-XML
 */

/*
 * Include necessary headers...
 */

#include "config.h"
#include "mxml.h"
#ifndef WIN32
#  include <unistd.h>
#endif /* !WIN32 */
#include <fcntl.h>
#ifndef O_BINARY
#  define O_BINARY 0
#endif /* !O_BINARY */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//#include "vfsfiles.h"
#include "menuxml.h"
#include <def.h>
//#include<Winsock2.h>

//#include <arpa/inet.h>
//#include <netinet/in.h>
static int menu_xml_inited = 0;
/*
 * Globals...
 */

int		event_counts[6];


/*
 * Local functions...
 */

void		sax_cb(mxml_node_t *node, mxml_sax_event_t event, void *data);
mxml_type_t	type_cb(mxml_node_t *node);
const char	*whitespace_cb(mxml_node_t *node, int where);


/*
 * 'main()' - Main entry for test program.
 */



/*
 * 'sax_cb()' - Process nodes via SAX.
 */

void
sax_cb(mxml_node_t      *node,		/* I - Current node */
       mxml_sax_event_t event,		/* I - SAX event */
       void             *data)		/* I - SAX user data */
{
 /*
  * This SAX callback just counts the different events.
  */

  event_counts[event] ++;
}


/*
 * 'type_cb()' - XML data type callback for mxmlLoadFile()...
 */

mxml_type_t				/* O - Data type */
type_cb(mxml_node_t *node)		/* I - Element node */
{
  const char	*type;			/* Type string */


 /*
  * You can lookup attributes and/or use the element name, hierarchy, etc...
  */

  if ((type = mxmlElementGetAttr(node, "type")) == NULL)
    type = node->value.element.name;

  if (!strcmp(type, "integer"))
    return (MXML_INTEGER);
  else if (!strcmp(type, "opaque") || !strcmp(type, "pre"))
    return (MXML_OPAQUE);
  else if (!strcmp(type, "real"))
    return (MXML_REAL);
  else
    return (MXML_TEXT);
}


/*
 * 'whitespace_cb()' - Let the mxmlSaveFile() function know when to insert
 *                     newlines and tabs...
 */

const char *				/* O - Whitespace string or NULL */
whitespace_cb(mxml_node_t *node,	/* I - Element node */
              int         where)	/* I - Open or close tag? */
{
  mxml_node_t	*parent;		/* Parent node */
  int		level;			/* Indentation level */
  const char	*name;			/* Name of element */
  static const char *tabs = "\t\t\t\t\t\t\t\t";
					/* Tabs for indentation */


 /*
  * We can conditionally break to a new line before or after any element.
  * These are just common HTML elements...
  */

  name = node->value.element.name;

  if (!strcmp(name, "html") || !strcmp(name, "head") || !strcmp(name, "body") ||
      !strcmp(name, "pre") || !strcmp(name, "p") ||
      !strcmp(name, "h1") || !strcmp(name, "h2") || !strcmp(name, "h3") ||
      !strcmp(name, "h4") || !strcmp(name, "h5") || !strcmp(name, "h6"))
  {
   /*
    * Newlines before open and after close...
    */

    if (where == MXML_WS_BEFORE_OPEN || where == MXML_WS_AFTER_CLOSE)
      return ("\n");
  }
  else if (!strcmp(name, "dl") || !strcmp(name, "ol") || !strcmp(name, "ul"))
  {
   /*
    * Put a newline before and after list elements...
    */

    return ("\n");
  }
  else if (!strcmp(name, "dd") || !strcmp(name, "dt") || !strcmp(name, "li"))
  {
   /*
    * Put a tab before <li>'s, <dd>'s, and <dt>'s, and a newline after them...
    */

    if (where == MXML_WS_BEFORE_OPEN)
      return ("\t");
    else if (where == MXML_WS_AFTER_CLOSE)
      return ("\n");
  }
  else if (!strncmp(name, "?xml", 4))
  {
    if (where == MXML_WS_AFTER_OPEN)
      return ("\n");
    else
      return (NULL);
  }
  else if (where == MXML_WS_BEFORE_OPEN ||
           ((!strcmp(name, "choice") || !strcmp(name, "option")) &&
	    where == MXML_WS_BEFORE_CLOSE))
  {
    for (level = -1, parent = node->parent;
         parent;
	 level ++, parent = parent->parent);

    if (level > 8)
      level = 8;
    else if (level < 0)
      level = 0;

    return (tabs + 8 - level);
  }
  else if (where == MXML_WS_AFTER_CLOSE ||
           ((!strcmp(name, "group") || !strcmp(name, "option") ||
	     !strcmp(name, "choice")) &&
            where == MXML_WS_AFTER_OPEN))
    return ("\n");
  else if (where == MXML_WS_AFTER_OPEN && !node->child)
    return ("\n");

 /*
  * Return NULL for no added whitespace...
  */

  return (NULL);
}


static int ipaddress=0xc0a80136;
static short ipport=10012;
/*
 * End of "$Id: testmxml.c 451 2014-01-04 21:50:06Z msweet $".
 */

#define PRINTN(depth) do{ \
					int _i; \
					for(_i=0;_i<depth;_i++)\
						printf("  "); \
					}while(0)




static const char *rt_xml_lang()
{
    u8 lindex;
    int temp_int;

    static const char *i18n[]={
        "en",/*english*/
        "cn",/*chinese*/
    };
    snmp_op_i_language(SNMP_GET, &temp_int);
    switch(temp_int)
    {
    case 1:
        lindex = 1;
        break;
    default:
        lindex = 0;
        break;
    }

    return i18n[lindex];

}
					

static struct xml_on *int_list, *int_last,int_face;

static struct xml_on *add_xml_hook(char *name,mxml_node_t *cur)
{
	struct xml_on *ife, **nextp, *new;

	for (ife = int_last; ife; ife = ife->prev) {
		int n = /*n*/strcmp(ife->name, name);

		if (n == 0)
			return ife;
		if (n < 0)
			break;
	}

	if(new = xzalloc(sizeof(*new))){
		strncpy(new->name, name,HOOKNAMSIZ);
		new->onhook = xml_hook_func(name);
		new->cur = cur;
		nextp = ife ? &ife->next : &int_list;
		new->prev = ife;
		new->next = *nextp;
		if (new->next)
			new->next->prev = new;
		else
			int_last = new;
		*nextp = new;
	}
	return new;
}

static struct curop curDo;

typedef enum{
	PARA_TYPE_IP,
	PARA_TYPE_NUM,
}para_type;

int strToInt(char strIP[],unsigned int *intIP)
{
    int length = strlen(strIP);
	int i=0,k=3,part=0;
	*intIP=0;
	for(i=0;i<=length;i++)
	{
		if(strIP[i]=='.' || strIP[i]=='\0')
			(*intIP) |= (part<<(k--*8)) , part=0;
		else if(strIP[i]>='0' && strIP[i]<='9')
			part=part*10+(strIP[i]-'0');
		else 
			return 0;	
	}
	return 1;
}

int putstr(char*str,int sLen)
{
	int i;
	for(i=0;i<sLen;i++)
		putchar(str[i]);
	putchar('\n');
}


static int xml_para_conf(mxml_node_t *cur,struct menu *mnBuf)
{
	struct paraIndex *para_t;

	if(para_t = (struct paraIndex*)cur->user_data)
		if(para_t->callback)
			para_t->callback(XML_SET,para_t,mnBuf);

}

static int xml_para_init(const char*attr,mxml_node_t*head,struct menu *mnBuf)
{
	struct paraIndex *para_t;

	head->user_data = (void *)xmlParaSerch(attr,head);
	if(head->user_data){

		head->data_type = PARA_TYPE_IP;
		para_t = (struct paraIndex*)head->user_data;
		if(para_t->callback)
			para_t->callback(XML_GET,para_t,mnBuf);
		
		if((attr = mxmlElementGetAttr (head, "type"))!=NULL){
			head->_private = (void *)xmlFuncSerch(attr);
		}
	}
}

static int xml_hook(mxml_node_t *head,struct menu *mnBuf)
{
	const char*attr;
	
	head->_private = NULL;
	head->user_data = NULL;

	if((attr = mxmlElementGetAttr (head, "hook"))!=NULL){

		head->_private = (void *)xmlFuncSerch(attr);
	}

	return (head->_private == NULL)?0:1;
}

static int xml_node_attr(mxml_node_t *head,struct menu *mnBuf)
{
	const char *attr,*i18n = rt_xml_lang(); 

	if(head->type == MXML_ELEMENT)  
	{
		//mnBuf->menuBuf[mnBuf->line].name[1] = ' ';
		
		attr = mxmlElementGetAttr (head, "text");
		if(attr)
			strncpy(&mnBuf->menuBuf[mnBuf->line].name[0],attr,38);
		else if((attr = mxmlElementGetAttr (head, i18n)) != NULL)
			strncpy(&mnBuf->menuBuf[mnBuf->line].name[0],attr,38);

		head->_private = NULL;

		if((attr = mxmlElementGetAttr (head, "id"))!=NULL){
			xml_para_init(attr,head,mnBuf);
		}

		mnBuf->line++;
	}
}

static int xml_option_para_get(mxml_node_t *head,struct menu *mnBuf)
{
	struct paraIndex* para_t;
	mxml_node_t *cur = head->child;
	const char *attr,*i18n = rt_xml_lang();

	char index_str[10];
	int ret=0,option =0;



	attr = mxmlElementGetAttr (head, "type");
	if((attr == NULL)||(cur == NULL))
		return -1;
	
	if(strcmp(attr,"option")!=0)
		return -2;
			
	if((attr = mxmlElementGetAttr (head, "id"))!=NULL){
		if(para_t = xmlParaSerch(attr,head)){
			if(para_t->callback(XML_GET,para_t,mnBuf)<0)
				return 0;
			
			ret = mnBuf->m_tmp;
		
			while(cur!=NULL){
				if(cur->type == MXML_ELEMENT){
					if((attr = mxmlElementGetAttr (cur, "value")))
						option = atoi(attr);
					
					if(option == ret){
						if((attr = mxmlElementGetAttr (cur, "text"))==NULL)
							attr = mxmlElementGetAttr (cur,i18n);
						
						if(attr){

							strncpy(&mnBuf->menuBuf[mnBuf->line].paraStr[0],
								attr,sizeof(mnBuf->menuBuf[mnBuf->line].paraStr));
							
							mnBuf->menuBuf[mnBuf->line].type= XML_NODE_OPTION;
							
							sprintf(index_str,"%d",ret);
							mxmlElementSetAttr(head,"value",index_str);
						}
						break;
					}
				}
				cur = cur->next;  
			}
		}
	}
	
	return 0;
}


static int xml_option_para_set(mxml_node_t *cur,struct menu *mnBuf)
{
    struct paraIndex *para_t;
    mxml_node_t *parent;
    const char *attr;

    
    if(parent = cur->parent){
        attr = mxmlElementGetAttr (parent, "type");
	
        if(attr && strcmp(attr,"option")==0){
            attr = mxmlElementGetAttr (parent, "id");
            
            if(para_t = xmlParaSerch(attr,parent)){
		if(para_t->callback){
			attr = mxmlElementGetAttr (cur, "value");
			mnBuf->m_tmp = atoi(attr);
			para_t->callback(XML_SET,para_t,mnBuf);
		}
            }
            return 0;
        }
    }
    return 1;
}

static void xml_menu_buf_clear(struct menu *mnBuf)
{
	mnBuf->attr_pos = 26;
	mnBuf->line = 0;
	mnBuf->maxIndex = 0;
	mnBuf->tLen = 0;
	mnBuf->arrow = 1;
	memset(mnBuf->menuBuf,0,sizeof(mnBuf->menuBuf));
}

static int xml_node_index(struct curop * curDo)
{
	int select,cur_l = 0,i,tmp=0,is_option = 0;
	mxml_node_t *head = curDo->curNode;//->children->next;
	struct menu *mnBuf;
	struct xml_on *on_event;
	
	const char *attr,*i18n = rt_xml_lang();

	mnBuf = &curDo->mnBuf;
	xml_menu_buf_clear(mnBuf);

	if(head != NULL){
		attr = mxmlElementGetAttr (head->parent,"type");
		if(attr && !strcmp(attr,"option")){
			if(attr = mxmlElementGetAttr (head->parent,"value")){
				select = atoi(attr);
				is_option = 1;
			}
		}
	}
	
	while(head != NULL)  
	{  
		//head = head->next;
		if(head->type == MXML_ELEMENT)  
		{  
			//PRINTN(depth);
			
			attr = mxmlElementGetAttr (head,"type");
			if(attr && !strcmp(attr,"ignore")){
				head = head->next; 
				continue;
			}
			
			if((attr = mxmlElementGetAttr (head, "onChange"))!=NULL){
				if(on_event = add_xml_hook(attr,head)){
					if(on_event->onhook)
						on_event->onhook(on_event);
				}
			}
			
			//printf("xml----> %s\n",head->value.element.name);
			if(head->child){
				//mnBuf->menuBuf[mnBuf->line].name[1] = ' ';
				if(attr = mxmlElementGetAttr (head,i18n)){
					strncpy(&mnBuf->menuBuf[mnBuf->line].name[0],attr,38);
				}
				mnBuf->menuBuf[mnBuf->line].type = XML_NODE;
				
				xml_option_para_get(head,mnBuf);
				mnBuf->line++;

			}else{
				if(is_option && (attr = mxmlElementGetAttr (head,"value"))){
					tmp = atoi(attr);
					if(tmp == select)
						cur_l = mnBuf->line;
				}
				xml_node_attr(head,mnBuf);
			}
			
			curDo->posNode[mnBuf->maxIndex] = head;
			mnBuf->maxIndex++;
			
		}  
		head = head->next;  
	}


	if(cur_l && (cur_l < mnBuf->maxIndex))
		mnBuf->index = cur_l;

	//printf("Node maxIndex:%d\n",mnBuf->maxIndex);
	return mnBuf->maxIndex;
}

void xml_index_str()
{
	int i;
	for(i=0;i<=curDo.depth;i++)
		printf("%d.",curDo.index_str[i]+1);
	printf("\n");
}


static mxml_node_t* xml_node_link(mxml_node_t *cur)
{
	mxml_node_t *current=NULL;
	const char *attr,*val;
	struct xml_on *on_event;

	if(curDo.link_depth >= LINK_DEPTH)
		return NULL;
	
	if(attr = mxmlElementGetAttr (cur, "onLink")){
		if(on_event = add_xml_hook(attr,cur)){
			if(on_event->onhook)
				on_event->onhook(on_event);
		}
	}
	
	if(attr = mxmlElementGetAttr (cur, "link")){
		
		if(val = mxmlElementGetAttr (cur, "value"))
			curDo.mnBuf.a_value = atoi(val);

            if(val = mxmlElementGetAttr (cur, "port"))
			curDo.mnBuf.port = atoi(val);
        
		current = mxmlFindElement(cur, curDo.headNode, attr, NULL, NULL, MXML_DESCEND);
		if(current){
			curDo.link_depth++;
			curDo.link[curDo.link_depth].linkBefore = cur;
			curDo.link[curDo.link_depth].linkAfter = current;
			current = current->child;
		}
	}	
	return current;
}

struct menu *xml_key_process(unsigned char key)
{
	mxml_node_t *current,*cur;
	static unsigned char edit=0;
	static int (*func)();
	const char * attr;
	int i;

	if(menu_xml_inited == 0)
		goto key_dis;
	
	if(edit){
	        if(func(key,&curDo.mnBuf,&edit)==0){
			if(edit == 0){
				cur = curDo.posNode[curDo.mnBuf.index];
				if(key == 'j')
                			xml_para_conf(cur,&curDo.mnBuf);
				
				curDo.mnBuf.edit = 0;
				xml_node_index(&curDo);/*refresh node data*/
			}
		}
		goto key_dis;
	}
	
	switch(key){
		case 'w':/* UP */
			if(curDo.mnBuf.index==0)
				curDo.mnBuf.index = (curDo.mnBuf.maxIndex-1);
			else
				curDo.mnBuf.index--;
			
			curDo.index_str[curDo.depth]=curDo.mnBuf.index;
			break;
		case 's':/* DOWN */
			if(curDo.mnBuf.index>=(curDo.mnBuf.maxIndex-1))
				curDo.mnBuf.index = 0;
			else
				curDo.mnBuf.index++;
			
			curDo.index_str[curDo.depth]=curDo.mnBuf.index;
			break;
		case 'a':/* LEFT */

			break;
		case 'd':/* RIGHT */

			break;
		case 'j':/* ENTER */
			if(curDo.mnBuf.index < curDo.mnBuf.maxIndex){
				cur = curDo.posNode[curDo.mnBuf.index];
				if(cur->child){
					curDo.index_str[curDo.depth++] = curDo.mnBuf.index;
					curDo.mnBuf.index = 0;
				
					if(attr = mxmlElementGetAttr (cur, "port"))
						curDo.mnBuf.port = atoi(attr);
					
					if(xml_hook(cur,&curDo.mnBuf)){
						if(func = cur->_private){
							func('i',&curDo.mnBuf,&edit);
							edit = 1;
						}
					}else{
						curDo.curNode = cur->child;
						xml_node_index(&curDo);
					}
				}else if((current = xml_node_link(cur)) != NULL){
					curDo.index_str[curDo.depth++] = curDo.mnBuf.index;
					curDo.curNode = current;
					curDo.mnBuf.index = 0;
					xml_node_index(&curDo);
				}else{
					
					if(xml_option_para_set(cur,&curDo.mnBuf) == 0){
		                        return xml_key_process('k');
					}
					
					if(func = cur->_private){
						edit = 1;
						curDo.mnBuf.edit=1;
					}else{
						edit = 0;
						curDo.mnBuf.edit=0;
					}
					
				}
			}
			break;
		case 'k':/* RETURN */
			
			if(curDo.curNode->parent &&(curDo.headNode != curDo.curNode->parent)){					
				if(curDo.curNode->parent == curDo.link[curDo.link_depth].linkAfter){
					if(curDo.link_depth > 0){
						curDo.curNode = curDo.link[curDo.link_depth].linkBefore;
						curDo.link_depth--;
					}
				}else{
					curDo.curNode = curDo.curNode->parent;
				}
				
				curDo.depth--;

				curDo.mnBuf.index = 0;
				while(curDo.curNode->prev != NULL){
					curDo.curNode = curDo.curNode->prev;					
					if(curDo.curNode->type == MXML_ELEMENT)  
						curDo.mnBuf.index++;
				}
				//printf("index:%d\n",curDo.mnBuf.index);
				sys_status_show_clear();
				xml_node_index(&curDo);
			}
			break;
		case 'l':/* LOCK */
			break;

		default:
			break;
	}
key_dis:
	return &curDo.mnBuf;
	//return xml_display(&curDo.mnBuf);

}

void xml_menu_refresh()
{
	xml_node_index(&curDo);
	//xml_display(&curDo.mnBuf);
}

#define filename "./lcd_menu.xml"
int menu_main()
{
	int			i;		/* Looping var */
	FILE			*fp;		/* File to read */

	mxml_node_t		*tree,		/* XML tree */
	*node;		/* Node which should be in test.xml */
	mxml_index_t		*ind;		/* XML index */
    //char			buffer[16384];	/* Save string */
	char key;
	static const char	*types[] =	/* Strings for node types */
			{
			  "MXML_ELEMENT",
			  "MXML_INTEGER",
			  "MXML_OPAQUE",
			  "MXML_REAL",
			  "MXML_TEXT"
			};


 /*
  * Check arguments...
  */
#if 0
    VFILE			*fd;		/* File descriptor */
    extern struct vfs_file vfsmenulist[];
    vfs_addfiles(vfsmenulist);
    fd = vfopen(filename,"rb");
    if(fd==NULL){
        printf("open file failed\n");
        return 0;
    }
	
	/*
	 * Read the file...
	 */

    tree = (mxml_node_t*)mxmlLoadvsFile(NULL, fd, type_cb);


#else
    memset(&curDo,0x00,sizeof(curDo));

    if ((fp = fopen(filename, "rb")) == NULL){
        printf("open menu xml file failed\n");

        strncpy(curDo.mnBuf.menuBuf[0].name,
			"open menu failed",
			sizeof(curDo.mnBuf.menuBuf[0].name));
        return -1;
    }



    tree = mxmlLoadFile(NULL, fp, type_cb);

    fclose(fp);
#endif
	
    node = tree->child;

    while(node!=NULL){
        if(node->type == MXML_ELEMENT)	
			break;
		node = node->next;
    }
	
	
    curDo.curNode  = node->child;
    curDo.headNode = node;
    xml_node_index(&curDo);

    menu_xml_inited = 1;
    printf("xml menu init:%d\n",menu_xml_inited);
    return 0;
}

int xml_save_file(mxml_node_t *tree)
{
	FILE *fp;

	fp = fopen("/tmp/lcd_menu.xml", "w");
	mxmlSaveFile(tree, fp, whitespace_cb);
	fclose(fp);

}

int xml_file_save()
{
	xml_save_file(curDo.headNode);

}

int xml_menu_type_ex(char *igstr,char *type)
{
	mxml_node_t *head = curDo.headNode->child;
	const char *attr;
	int ret = -1;
	
	while(head != NULL)  
	{  
		if(head->type == MXML_ELEMENT)  
		{  			
			attr = mxmlElementGetAttr (head,"id");
			if(attr && !strcmp(attr,igstr)){
				mxmlElementSetAttr(head,"type",type);
				ret = 0;
				break;
			}
		}  
		head = head->next;  
	}
	return ret;
}

